#include "simodo/variable/json/Rpc.h"
#include "simodo/variable/json/Serialization.h"
#include "simodo/inout/convert/functions.h"

namespace simodo::variable
{

JsonRpc::JsonRpc(const std::string & json)
    : _origin_json(json)
    , _value(fromJson(json))
{
    if (_value.type() != ValueType::Object)
        return;

    setupMembers();
}

JsonRpc::JsonRpc(const std::u16string & json)
    : _origin_json(inout::toU8(json))
    , _value(fromJson(json))
{
    if (_value.type() != ValueType::Object)
        return;

    setupMembers();
}

JsonRpc::JsonRpc(variable::Value value)
    : _origin_json(toJson(value))
    , _value(value)
{
    if (_value.type() != ValueType::Object)
        return;

    setupMembers();
}

JsonRpc::JsonRpc(std::u16string method, variable::Value params, int64_t id)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"method", method},
        {u"params", params},
        {u"id", id},
    }})
{
    setupMembers();
}

JsonRpc::JsonRpc(std::u16string method, int64_t id)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"method", method},
        {u"id", id},
    }})
{
    setupMembers();
}

JsonRpc::JsonRpc(std::u16string method, variable::Value params)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"method", method},
        {u"params", params},
    }})
{
    setupMembers();
}

// Rpc::Rpc(std::u16string method)
//     : _value(Record {{
//         {u"jsonrpc_version", u"2.0"},
//         {u"method", method},
//     }})
// {
//     setupMembers();
// }

JsonRpc::JsonRpc(variable::Value result, int64_t id)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"result", result},
        {u"id", id},
    }})
{
    setupMembers();
}

JsonRpc::JsonRpc(int64_t code, std::u16string message, variable::Value data, int64_t id)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"error", Object {{
            {u"code",    code},
            {u"message", message},
            {u"data",    data},
        }}},
        {u"id", id},
    }})
{
    setupMembers();
}

JsonRpc::JsonRpc(int64_t code, std::u16string message, int64_t id)
    : _value(Object {{
        {u"jsonrpc_version", u"2.0"},
        {u"error", Object {{
            {u"code",    code},
            {u"message", message},
        }}},
        {u"id", id},
    }})
{
    setupMembers();
}

const variable::Value & JsonRpc::params() const
{
    return _params ? *_params : _fail;
}

const variable::Value & JsonRpc::result() const
{
    return _result ? *_result : _fail;
}

const variable::Value & JsonRpc::error() const
{
    return _error ? *_error : _fail;
}

void JsonRpc::setupMembers()
{
    if (_is_valid)
        return;

    const auto object = _value.getObject();
    const Value & jsonrpc_value = object->find(u"jsonrpc_version");
    const Value & method_value  = object->find(u"method");
    const Value & id_value      = object->find(u"id");
    const Value & params_value  = object->find(u"params");
    const Value & result_value  = object->find(u"result");
    const Value & error_value   = object->find(u"error");

    if (jsonrpc_value.type() == ValueType::String)
        _jsonrpc_version = jsonrpc_value.getString();
    else
        _jsonrpc_version = u"1.0";
    if (method_value.type() == ValueType::String)
        _method = method_value.getString();
    if (id_value.type() == ValueType::Int)
        _id = id_value.getInt();
    if (params_value.type() == ValueType::Array || params_value.type() == ValueType::Object)
        _params = &params_value;
    if (!result_value.isNull())
        _result = &result_value;
    if (error_value.type() == ValueType::Object)
        _error = &error_value;

    if (_origin_json.empty())
        _origin_json = toJson(_value);

    if (!_method.empty() && (_result || _error))
        return;

    if (_method.empty() && !_result && !_error && _id < 0)
        return;

    if (_result && _error)
        return;

    if ((_result || _error) && _params)
        return;

    _is_valid = true;
}

}
